home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 098 / grep.cq / grep.c
Text File  |  1980-01-01  |  17KB  |  666 lines

  1.  
  2. /*
  3.  *
  4.  *
  5.  * The  information  in  this  document  is  subject  to  change
  6.  * without  notice  and  should not be construed as a commitment
  7.  * by Digital Equipment Corporation or by DECUS.
  8.  *
  9.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  10.  * assume any responsibility for the use or reliability of  this
  11.  * document or the described software.
  12.  *
  13.  *      Copyright (C) 1980, DECUS
  14.  *
  15.  *
  16.  * General permission to copy or modify, but not for profit,  is
  17.  * hereby  granted,  provided that the above copyright notice is
  18.  * included and reference made to  the  fact  that  reproduction
  19.  * privileges were granted by DECUS.
  20.  *
  21.  */
  22.  
  23. #include "stdio.h"
  24. #include "ctype.h"
  25.  
  26. /*
  27.  * grep.
  28.  *
  29.  * Runs on the Decus compiler or on vms.
  30.  * Converted for BDS compiler (under CP/M-80), 20-Jan-83, by Chris Kern.
  31.  *
  32.  * Converted to IBM PC with CI-C86 C Compiler June 1983 by David N. Smith
  33.  *
  34.  * Help paragraphs justified on April 1985 by Phil L. Kim.
  35.  *
  36.  * On vms, define as:
  37.  *
  38.  *      grep :== "$disk:[account]grep"     (native)
  39.  *      grep :== "$disk:[account]grep grep"     (Decus)
  40.  *
  41.  * See below for more information.
  42.  *
  43.  */
  44.  
  45.  
  46. char    *documentation[] = {
  47. "grep searches a file for a given pattern.  Execute by",
  48. "   grep [flags] regular_expression file_list",
  49. "",
  50. "Flags are single characters preceeded by '-':",
  51. "   -c      Only a count of matching lines is printed",
  52. "   -f      Print file name for matching lines switch, see below",
  53. "   -n      Each line is preceeded by its line number",
  54. "   -v      Only print non-matching lines",
  55. "",
  56. "The file_list is a list of files (wildcards are acceptable on RSX modes).",
  57. "",
  58. "The file name is normally printed if there is a file given.",
  59. "The -f flag reverses this action (print name no file, not if more).",
  60. "",
  61. 0 };
  62.  
  63.  
  64. char    *patdoc[] = {
  65.  
  66. "The regular_expression defines the pattern  to  search  for.  Upper-  and",
  67. "lower-case  are always ignored.  Blank lines never match.  The expression",
  68. "should be quoted to prevent file-name translation.",
  69. "x      An ordinary character (not mentioned below) matches that character.",
  70. "'\\'    The backslash quotes any character.  \"\\$\" matches a dollar-sign.",
  71. "'^'    A circumflex  at  the  beginning  of  an  expression  matches  the",
  72. "       beginning of a line.",
  73. "'$'    A dollar-sign at the end of an expression matches the end of a line.",
  74. "'.'    A period matches any character except \"new-line\".",
  75. "':a'   A colon matches a class of characters described by the following",
  76. "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,",
  77. "':n'     \":n\" matches alphanumerics, \": \" matches spaces, tabs, and",
  78. "': '     other control characters, such as new-line.",
  79. "'*'    An  expression  followed  by  an  asterisk  matches  zero  or more",
  80. "       occurrances of that expression:  \"fo*\" matches  \"f\", \"fo\", \"foo\",",
  81. "       etc.",
  82. "'+'    An  expression  followed  by  a  plus  sign  matches  one  or more",
  83. "       occurrances of that expression: \"fo+\" matches \"fo\", etc.",
  84. "'-'    An  expression  followed  by  a  minus sign optionally matches the",
  85. "       expression.",
  86. "'[]'···A string enclosed in square brackets matches any character in that",
  87. "       string,  but no others.  If the first character in the string is a",
  88. "       circumflex, the expression matches any character except \"new-line\"",
  89. "       and  the  characters in the string.  For example,  \"[xyz]\" matches",
  90. "       \"xx\" and \"zyx\",  while \"[^xyz]\" matches \"abc\"  but  not  \"axb\".  A",
  91. "       range  of  characters may be specified by two characters separated",
  92. "       by \"-\".  Note that,  [a-z] matches alphabetics,  while [z-a] never",
  93. "       matches.",
  94. "The concatenation of regular expressions is a regular expression.",
  95. 0};
  96.  
  97. #ifndef stdin
  98. #define stdin STDIN
  99. #define stdout STDOUT
  100. #define stderr STDERR
  101. #define DeSmet 1
  102. #endif
  103.  
  104. #define LMAX    512
  105. #define PMAX    256
  106.  
  107. #define CHAR    1
  108. #define BOL     2
  109. #define EOL     3
  110. #define ANY     4
  111. #define CLASS   5
  112. #define NCLASS  6
  113. #define STAR    7
  114. #define PLUS    8
  115. #define MINUS   9
  116. #define ALPHA   10
  117. #define DIGIT   11
  118. #define NALPHA  12
  119. #define PUNCT   13
  120. #define RANGE   14
  121. #define ENDPAT  15
  122.  
  123. int     cflag;
  124. int     fflag;
  125. int     nflag;
  126. int     vflag;
  127. int     nfile;
  128.  
  129. int     debug   =       0;         /* Set for debug code      */
  130.  
  131. char    *pp;
  132.  
  133. #ifndef vms
  134. char    file_name[81];
  135. #endif
  136.  
  137. char    lbuf[LMAX];
  138. char    pbuf[PMAX];
  139.  
  140. /*******************************************************/
  141.  
  142. main(argc, argv)
  143. char *argv[];
  144. {
  145.    register char   *p;
  146.    register int    c, i;
  147.    int             gotpattern;
  148.    int             gotcha;
  149.  
  150.    FILE            *f;
  151.  
  152.    if (argc <= 1)
  153.       usage("No arguments");
  154.    if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  155.       help(documentation);
  156.       help(patdoc);
  157.       return;
  158.       }
  159.    nfile = argc-1;
  160.    gotpattern = 0;
  161.    for (i=1; i < argc; ++i) {
  162.       p = argv[i];
  163.       if (*p == '-') {
  164.          ++p;
  165.          while (c = *p++) {
  166.             switch(tolower(c)) {
  167.  
  168.             case '?':
  169.                help(documentation);
  170.                break;
  171.  
  172.             case 'C':
  173.             case 'c':
  174.                ++cflag;
  175.                break;
  176.  
  177.             case 'D':
  178.             case 'd':
  179.                ++debug;
  180.                break;
  181.  
  182.             case 'F':
  183.             case 'f':
  184.                ++fflag;
  185.                break;
  186.  
  187.             case 'n':
  188.             case 'N':
  189.                ++nflag;
  190.                break;
  191.  
  192.             case 'v':
  193.             case 'V':
  194.                ++vflag;
  195.                break;
  196.  
  197.             default:
  198.                usage("Unknown flag");
  199.             }
  200.          }
  201.          argv[i] = 0;
  202.          --nfile;
  203.       } else if (!gotpattern) {
  204.          compile(p);
  205.          argv[i] = 0;
  206.          ++gotpattern;
  207.          --nfile;
  208.       }
  209.    }
  210.    if (!gotpattern)
  211.       usage("No pattern");
  212.    if (nfile == 0)
  213.       grep(stdin, 0);
  214.    else {
  215.       fflag = fflag ^ (nfile > 0);
  216.       for (i=1; i < argc; ++i) {
  217.          if (p = argv[i]) {
  218.             if ((f=fopen(p, "r")) == NULL)
  219.                cant(p);
  220.             else {
  221.                grep(f, p);
  222.                fclose(f);
  223.             }
  224.          }
  225.       }
  226.    }
  227. }
  228.  
  229. /*******************************************************/
  230.  
  231. file(s)
  232. char *s;
  233. {
  234.    printf("File %s:\n", s);
  235. }
  236.  
  237. /*******************************************************/
  238.  
  239. cant(s)
  240. char *s;
  241. {
  242.    fprintf(stderr, "%s: cannot open\n", s);
  243. }
  244.  
  245.  
  246. /*******************************************************/
  247.  
  248. help(hp)
  249. char **hp;  /* dns added extra '*'  */
  250. /*
  251.  * Give good help
  252.  */
  253. {
  254.    register char   **dp;
  255.  
  256.    for (dp = hp; *dp; dp++)
  257.       printf("%s\n", *dp);
  258. }
  259.  
  260.  
  261. /*******************************************************/
  262.  
  263. usage(s)
  264. char    *s;
  265. {
  266.    fprintf(stderr, "Error: GREP-E-%s\n", s);
  267.    fprintf(stderr, "\tUsage: grep [-cfnv] pattern [file ...].\n");
  268.    fprintf(stderr, "GREP ? for help\n");
  269.    exit(1);
  270. }
  271.  
  272. /*******************************************************/
  273.  
  274. compile(source)
  275. char       *source;   /* Pattern to compile         */
  276. /*
  277.  * Compile the pattern into global pbuf[]
  278.  */
  279. {
  280.    register char  *s;         /* Source string pointer     */
  281.    register char  *lp;        /* Last pattern pointer      */
  282.    register int   c;          /* Current character         */
  283.    int            o;          /* Temp                      */
  284.    char           *spp;       /* Save beginning of pattern */
  285.    char           *cclass();  /* Compile class routine     */
  286.  
  287.    s = source;
  288.    if (debug)
  289.       printf("Pattern = \"%s\"\n", s);
  290.    pp = pbuf;
  291.    while (c = *s++) {
  292.       /*
  293.        * STAR, PLUS and MINUS are special.
  294.        */
  295.       if (c == '*' || c == '+' || c == '-') {
  296.          if (pp == pbuf ||
  297.               (o=pp[-1]) == BOL ||
  298.               o == EOL ||
  299.               o == STAR ||
  300.               o == PLUS ||
  301.               o == MINUS)
  302.             badpat("Illegal occurrance op.", source, s);
  303.          store(ENDPAT);
  304.          store(ENDPAT);
  305.          spp = pp;               /* Save pattern end     */
  306.          while (--pp > lp)       /* Move pattern down    */
  307.             *pp = pp[-1];        /* one byte             */
  308.          *pp =   (c == '*') ? STAR :
  309.             (c == '-') ? MINUS : PLUS;
  310.          pp = spp;               /* Restore pattern end  */
  311.          continue;
  312.       }
  313.       /*
  314.        * All the rest.
  315.        */
  316.       lp = pp;         /* Remember start       */
  317.       switch(c) {
  318.  
  319.       case '^':
  320.          store(BOL);
  321.          break;
  322.  
  323.       case '$':
  324.          store(EOL);
  325.          break;
  326.  
  327.       case '.':
  328.          store(ANY);
  329.          break;
  330.  
  331.       case '[':
  332.          s = cclass(source, s);
  333.          break;
  334.  
  335.       case ':':
  336.          if (*s) {
  337.             c = *s++;
  338.             switch(tolower(c)) {
  339.  
  340.             case 'a':
  341.             case 'A':
  342.                store(ALPHA);
  343.                break;
  344.  
  345.             case 'd':
  346.             case 'D':
  347.                store(DIGIT);
  348.                break;
  349.  
  350.             case 'n':
  351.             case 'N':
  352.                store(NALPHA);
  353.                break;
  354.  
  355.             case ' ':
  356.                store(PUNCT);
  357.                break;
  358.  
  359.             default:
  360.                badpat("Unknown : type", source, s);
  361.  
  362.             }
  363.             break;
  364.          }
  365.          else    badpat("No : type", source, s);
  366.  
  367.       case '\\':
  368.          if (*s)
  369.             c = *s++;
  370.  
  371.       default:
  372.          store(CHAR);
  373.          store(tolower(c));
  374.       }
  375.    }
  376.    store(ENDPAT);
  377.    store(0);                /* Terminate string     */
  378.    if (debug) {
  379.       for (lp = pbuf; lp < pp;) {
  380.          if ((c = (*lp++ & 0377)) < ' ')
  381.             printf("\\%o ", c);
  382.          else    printf("%c ", c);
  383.         }
  384.         printf("\n");
  385.    }
  386. }
  387.  
  388. /*******************************************************/
  389.  
  390. char *
  391. cclass(source, src)
  392. char       *source;   /* Pattern start -- for error msg.      */
  393. char       *src;      /* Class start           */
  394. /*
  395.  * Compile a class (within [])
  396.  */
  397. {
  398.    register char   *s;        /* Source pointer    */
  399.    register char   *cp;       /* Pattern start     */
  400.    register int    c;         /* Current character */
  401.    int             o;         /* Temp              */
  402.  
  403.    s = src;
  404.    o = CLASS;
  405.    if (*s == '^') {
  406.       ++s;
  407.       o = NCLASS;
  408.    }
  409.    store(o);
  410.    cp = pp;
  411.    store(0);                          /* Byte count      */
  412.    while ((c = *s++) && c!=']') {
  413.       if (c == '\\') {                /* Store quoted char    */
  414.          if ((c = *s++) == '\0')      /* Gotta get something  */
  415.             badpat("Class terminates badly", source, s);
  416.          else    store(tolower(c));
  417.       }
  418.       else if (c == '-' &&
  419.             (pp - cp) > 1 && *s != ']' && *s != '\0') {
  420.          c = pp[-1];             /* Range start     */
  421.          pp[-1] = RANGE;         /* Range signal    */
  422.          store(c);               /* Re-store start  */
  423.          c = *s++;               /* Get end char and*/
  424.          store(tolower(c));      /* Store it        */
  425.       }
  426.       else {
  427.          store(tolower(c));      /* Store normal char */
  428.       }
  429.    }
  430.    if (c != ']')
  431.       badpat("Unterminated class", source, s);
  432.    if ((c = (pp - cp)) >= 256)
  433.       badpat("Class too large", source, s);
  434.    if (c == 0)
  435.       badpat("Empty class", source, s);
  436.    *cp = c;
  437.    return(s);
  438. }
  439.  
  440. /*******************************************************/
  441.  
  442. store(op)
  443. {
  444.    if (pp >= &pbuf[PMAX])
  445.       error("Pattern too complex\n");
  446.    *pp++ = op;
  447. }
  448.  
  449.  
  450. /*******************************************************/
  451.  
  452. badpat(message, source, stop)
  453. char  *message;       /* Error message */
  454. char  *source;        /* Pattern start */
  455. char  *stop;          /* Pattern end   */
  456. {
  457.    register int    c;
  458.  
  459.    fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  460.    fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  461.          stop-source, stop[-1]);
  462.    error("?GREP-E-Bad pattern\n");
  463. }
  464.  
  465.  
  466.  
  467. /*******************************************************/
  468.  
  469. grep(fp, fn)
  470. FILE       *fp;       /* File to process            */
  471. char       *fn;       /* File name (for -f option)  */
  472. /*
  473.  * Scan the file for the pattern in pbuf[]
  474.  */
  475. {
  476.    register int lno, count, m;
  477.  
  478.    lno = 0;
  479.    count = 0;
  480.    while (fgets(lbuf, LMAX, fp)) {
  481.       ++lno;
  482.       m = match();
  483.       if ((m && !vflag) || (!m && vflag)) {
  484.          ++count;
  485.          if (!cflag) {
  486.             if (fflag && fn) {
  487.                file(fn);
  488.                fn = 0;
  489.             }
  490.             if (nflag)
  491.                printf("%d\t", lno);
  492.             printf("%s\n", lbuf);
  493.          }
  494.       }
  495.    }
  496.    if (cflag) {
  497.       if (fflag && fn)
  498.          file(fn);
  499.       printf("%d\n", count);
  500.    }
  501. }
  502.  
  503.  
  504. /*******************************************************/
  505.  
  506. match()
  507. /*
  508.  * Match the current line (in lbuf[]), return 1 if it does.
  509.  */
  510. {
  511.    register char   *l;        /* Line pointer       */
  512.    char *pmatch();
  513.  
  514.    for (l = lbuf; *l; l++) {
  515.       if (pmatch(l, pbuf))
  516.          return(1);
  517.    }
  518.    return(0);
  519. }
  520.  
  521. /*******************************************************/
  522.  
  523. char *
  524. pmatch(line, pattern)
  525. char               *line;     /* (partial) line to match      */
  526. char               *pattern;  /* (partial) pattern to match   */
  527. {
  528.    register char   *l;        /* Current line pointer         */
  529.    register char   *p;        /* Current pattern pointer      */
  530.    register char   c;         /* Current character            */
  531.    char            *e;        /* End for STAR and PLUS match  */
  532.    int             op;        /* Pattern operation            */
  533.    int             n;         /* Class counter                */
  534.    char            *are;      /* Start of STAR match          */
  535.  
  536.    l = line;
  537.    if (debug > 1)
  538.       printf("pmatch(\"%s\")\n", line);
  539.    p = pattern;
  540.    while ((op = *p++) != ENDPAT) {
  541.       if (debug > 1)
  542.          printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  543.                l-line, *l, *l, op);
  544.       switch(op) {
  545.  
  546.       case CHAR:
  547.          if (tolower(*l++) != *p++)
  548.             return(0);
  549.          break;
  550.  
  551.       case BOL:
  552.          if (l != lbuf)
  553.             return(0);
  554.          break;
  555.  
  556.       case EOL:
  557.          if (*l != '\0')
  558.             return(0);
  559.          break;
  560.  
  561.       case ANY:
  562.          if (*l++ == '\0')
  563.             return(0);
  564.          break;
  565.  
  566.       case DIGIT:
  567.          if ((c = *l++) < '0' || (c > '9'))
  568.             return(0);
  569.          break;
  570.  
  571.       case ALPHA:
  572.          c = tolower(*l++);
  573.          if (c < 'a' || c > 'z')
  574.             return(0);
  575.          break;
  576.  
  577.       case NALPHA:
  578.          c = tolower(*l++);
  579.          if (c >= 'a' && c <= 'z')
  580.             break;
  581.          else if (c < '0' || c > '9')
  582.             return(0);
  583.          break;
  584.  
  585.       case PUNCT:
  586.          c = *l++;
  587.          if (c == 0 || c > ' ')
  588.             return(0);
  589.          break;
  590.  
  591.       case CLASS:
  592.       case NCLASS:
  593.          c = tolower(*l++);
  594.          n = *p++ & 0377;
  595.          do {
  596.             if (*p == RANGE) {
  597.                p += 3;
  598.                n -= 2;
  599.                if (c >= p[-2] && c <= p[-1])
  600.                   break;
  601.             }
  602.             else if (c == *p++)
  603.                break;
  604.          } while (--n > 1);
  605.          if ((op == CLASS) == (n <= 1))
  606.             return(0);
  607.          if (op == CLASS)
  608.             p += n - 2;
  609.          break;
  610.  
  611.       case MINUS:
  612.          e = pmatch(l, p);       /* Look for a match    */
  613.          while (*p++ != ENDPAT); /* Skip over pattern   */
  614.          if (e)                  /* Got a match?        */
  615.             l = e;               /* Yes, update string  */
  616.          break;                  /* Always succeeds     */
  617.  
  618.       case PLUS:                 /* One or more ...     */
  619.          if ((l = pmatch(l, p)) == 0)
  620.             return(0);           /* Gotta have a match  */
  621.       case STAR:                 /* Zero or more ...    */
  622.          are = l;                /* Remember line start */
  623.          while (*l && (e = pmatch(l, p)))
  624.             l = e;               /* Get longest match   */
  625.          while (*p++ != ENDPAT); /* Skip over pattern   */
  626.          while (l >= are) {      /* Try to match rest   */
  627.             if (e = pmatch(l, p))
  628.                return(e);
  629.             --l;                 /* Nope, try earlier   */
  630.          }
  631.          return(0);              /* Nothing else worked */
  632.  
  633.       default:
  634.          printf("Bad op code %d\n", op);
  635.          error("Cannot happen -- match\n");
  636.       }
  637.    }
  638.    return(l);
  639. }
  640.  
  641. /*******************************************************/
  642.  
  643. error(s)
  644. char *s;
  645. {
  646.    fprintf(stderr, "%s", s);
  647.    exit(1);
  648. }
  649.  
  650. /*******************************************************/
  651.  
  652. #ifdef DeSmet
  653. fclose(file)
  654. FILE file;
  655. {
  656.      return(close(file));
  657. }
  658. #endif
  659. ***************************************/
  660.  
  661. #ifdef DeSmet
  662. fclose(file)
  663. FILE file;
  664. {
  665.